home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_c / natcomp / compile.cb next >
Text File  |  1990-03-13  |  16KB  |  561 lines

  1. /*
  2.  *        BRIEF -- Basic Reconfigurable Interactive Editing Facility
  3.  *
  4.  *        Written by Dave Nanian and Michael Strickman.
  5.  *
  6.  *        1-14-90        Nat    added code to translate environment variables
  7.  *                                which may be passed in on compile command line
  8.  */
  9.  
  10. /*
  11.  *        compile.cb:
  12.  *
  13.  *        This file contains all of the standard BRIEF macros for compiling files.
  14.  */
  15.  
  16. string trans_envar (string cmdln);                    // NAT - translates out env var's
  17. string add_to_path (string path, string name);
  18. string escape_re (string original, ~string);
  19. int next_error (~int action);
  20. int cc (string pass_string, ~string legal_extension, ~int check_warnings, ~int background, ~int continuation);
  21. void _cc_complete (int ret_code, string full_name, int check_warnings, string pass_string);
  22. int cm (...);
  23. int cb (...);
  24.  
  25. extern int    _check_warnings,    //    Check for warnings in the compiler output?
  26.                 _background;        //    Run the compiler in the background?
  27.  
  28. /*
  29.  *        compile_it:
  30.  *
  31.  *        This function automatically compiles the file in the current
  32.  *    buffer.    It uses the "BC<extension>" environment variable to
  33.  *    determine what to do with any given file.  If no "BC<extension>"
  34.  *    environment variable exists for the specific file extension being
  35.  *    compiled, compile_it checks to see if it's ".c", ".m" or ".asm".    If
  36.  *    it's a macro file the "cm" macro is executed; if it's a C file, a
  37.  *    generic "cc" command is used, and if it's an ASM file, the Macro
  38.  *    Assembler is invoked.
  39.  *
  40.  *        Other compilers can be supported very easily.  Simply set a
  41.  *    "BC<extension>" environment variable to the "pass string" you
  42.  *    want to use.  For example, if you wanted to call the (fictitious)
  43.  *    UnderWare C compiler, which has two passes called "under" and "ware",
  44.  *    you'd use the command:
  45.  *
  46.  *        set bcc="under %s;ware %s"
  47.  *
  48.  *        You must place the pass string in quotes.  If you don't, compile_it
  49.  *    calls a macro named whatever is in the pass string.  So, for example,
  50.  *    if you accidently set your pass string using the command:
  51.  *
  52.  *        set bcc=under %s;ware %s
  53.  *
  54.  *        compile_it would call a macro named "under %s;ware %s".    This probably
  55.  *    isn't what you want -- but this feature can be useful if you want to
  56.  *    run some sort of custom macro for the file extension (compile_it does
  57.  *    this when compiling cm files).
  58.  *
  59.  *        Also note that DOS requires you to double any % characters that
  60.  *    appear in a batch file.  So if you were setting the BCC variable in
  61.  *    your autoexec, you would use the line:
  62.  *
  63.  *        set bcc="under %%s;ware %%s"
  64.  *
  65.  *        Each pass begins with the name of the executable program that does
  66.  *    that compilation pass.    That is followed by the a space, the special
  67.  *    string "%s", which is replaced by the filename (with NO extension),
  68.  *    and the multiple pass separation character ";".  If you want to put
  69.  *    a ";" in your pass string, use "\;".
  70.  *
  71.  *        These special characters are very important -- don't forget them!
  72.  *    Remember that the special "%s" string is only replaced by the filename,
  73.  *    not the filename and the extension.  Up to seven of these can be specified
  74.  *    in any given pass.  If your compiler requires the extension as well,
  75.  *    place it after this string (e.g. "cc -c %s.c").
  76.  *
  77.  *        If you want to pass options to your compiler, you can place them
  78.  *    either before or after the "%s".  Placing them before puts the option
  79.  *    before the filename, and vice versa.
  80.  *
  81.  *        If your compiler doesn't return an error code, put an exclamation
  82.  *    point in front of the first pass string (either inside or outside the
  83.  *    quotes); this will override the current warnings_only setting and
  84.  *    automatically check for errors in the compiler output.
  85.  */
  86.             
  87. int compile_it (void)
  88. {
  89.     string    extension,        //    File extension of the current file.
  90.                 command;            //    Command to be used to compile file.
  91.  
  92.     inq_names (NULL, extension);
  93.     command = trans_envar( inq_environment( "BC" + upper (extension) ) );
  94.     if( !get_parm( 0, command, "Compile with: ", 80, command ) )
  95.         return( 0 );
  96.  
  97.     if (command != "")
  98.     {
  99.         if (index (command, "\""))
  100.         {
  101.             int    loc,
  102.                     background;
  103.  
  104.             background = _background;
  105.  
  106.             while (loc = index (command, "\""))
  107.                 command = substr (command, 1, loc - 1) + substr (command, loc + 1);
  108.  
  109.             if (substr (command, strlen (command), 1) == "&")
  110.             {
  111.                 background = 1;
  112.                 command = trim (substr (command, 1, strlen (command) - 1));
  113.             }
  114.             if ("!" == substr (command, 1, 1))
  115.                 returns (cc (substr (command, 2), extension, 1, background));
  116.             else
  117.                 returns (cc (command, extension, NULL, background));
  118.         }
  119.         else
  120.         {
  121.             int    check_warnings = _check_warnings;
  122.  
  123.             if (substr (command, 1, 1) == "!")
  124.             {
  125.                 check_warnings = 1;
  126.                 command = substr (command, 2);
  127.             }
  128.             returns (execute_macro (command, command, check_warnings));
  129.         }
  130.     }
  131.     else
  132.         switch (extension)
  133.         {
  134.             case "m":
  135.                 returns (cm (_check_warnings));
  136.  
  137.             case "asm":
  138.                 returns (cc ("masm %s\\;", extension));
  139.  
  140.             case "c":
  141.                 returns (cc ("cc -c %s.c", extension));
  142.  
  143.             case "cb":
  144.                 returns (cb (_check_warnings));
  145.  
  146.             default:
  147.             {
  148.                 error ("Can't compile: no BC%s environment variable.", upper (extension));
  149.                 returns (-1);
  150.             }
  151.         }
  152. }
  153.  
  154. /*
  155.  *        warnings_only:
  156.  *
  157.  *        This macro toggles whether or not errors are searched for when a
  158.  *    compile is done and the compiler returns "no errors".  Note that its
  159.  *    value is saved in the state file.
  160.  */
  161.  
  162. int warnings_only (~int)
  163. {
  164.     int    ret_code,
  165.             previous_value;
  166.  
  167.     previous_value = _check_warnings;
  168.  
  169.     if (!(ret_code = get_parm (0, _check_warnings)))
  170.         _check_warnings = !_check_warnings;
  171.  
  172.     if (!ret_code || inq_called () == "")
  173.         message ("Compile warning detection %s.", _check_warnings ? "on" : "off");
  174.  
  175.     returns (previous_value);
  176. }
  177.  
  178. /*
  179.  *        bgd_compilation:
  180.  *
  181.  *        This macro toggles whether or not compilation is done in the
  182.  *    background under operating systems that support it.  Note that this
  183.  *    value is saved in the state file.
  184.  */
  185.  
  186. int bgd_compilation (~int)
  187. {
  188.     int    ret_code,
  189.             previous_value;
  190.  
  191.     previous_value = _background;
  192.  
  193.     if (!(ret_code = get_parm (0, _background)))
  194.         _background = !_background;
  195.  
  196.     if (!ret_code || inq_called () == "")
  197.         message ("Background compilation %s.", _background ? "on" : "off");
  198.  
  199.     returns (previous_value);
  200. }
  201.  
  202. /*
  203.  *        cc:
  204.  *
  205.  *        This routine compiles the file in the current buffer using the
  206.  *    passed "pass string" and the BRIEF DOS command.  It needs a lot
  207.  *    of memory to run -- either turn swapping on or start with at least
  208.  *    256K and -M20.  Of course, this may vary depending with the size
  209.  *    and memory requirements of the specific compiler you're using.
  210.  *
  211.  *        The "pass string" passed should be of the form:
  212.  *
  213.  *            pass_1 %s;pass_2 %s;...pass_n %s
  214.  *
  215.  *        The optional second parameter is an extended file type -- this is
  216.  *    used by the "cm" and "cb" macros, and to compile other types of files
  217.  *    (e.g. .asm).
  218.  *
  219.  *        If no pass string is specified, it defaults to a generic "cc"
  220.  *    command.  If no extension is specified, it defaults to "c".
  221.  */
  222.  
  223. int cc (string pass_string, ~string legal_extension, ~int, ~int, ~int continuation)
  224. {
  225.     string    file_name,            //    The name of the file we're compiling.
  226.                 path,                    //    The path of the file we're compiling.
  227.                 extension,            //    The extension of the file we're compiling.
  228.                 command_line,         //    The compile command line.
  229.                 old_path,            //    The original path we were on.
  230.                 error_file;            //    The file to put error information in.
  231.  
  232.     int    loc,                         //    Generic index place holder.
  233.             length,                     //    Generic length place holder.
  234.             ret_code = 1,            //    Return code from DOS.
  235.             buffer_id,                 //    Buffer ID of error buffer.
  236.             check_warnings,        //    Examine result of compile for errors?
  237.             background;                //    Do compilation in background?
  238.  
  239.     /*
  240.      *        We get the name of the file from inq_names and check to see
  241.      *    if it's a legal extension.
  242.      */
  243.  
  244.     if (legal_extension == "")
  245.         legal_extension = "c";
  246.  
  247.     inq_names (path, extension, file_name);
  248.  
  249.     if (extension != legal_extension)
  250.         error ("Current buffer is not a .%s file.", pass_string);
  251.     else
  252.     {
  253.         /*
  254.          *        If the file has been modified, we want to make sure the
  255.          *    current version gets compiled, so we write it to disk.
  256.          *
  257.          *        Note that if the user does not specify a pass string, it
  258.          *    defaults to a generic "cc" command.
  259.           */
  260.  
  261.         if (pass_string == "")
  262.             pass_string = "cc -c %s.c";
  263.  
  264.         if (!get_parm (2, check_warnings))
  265.             check_warnings = _check_warnings;
  266.  
  267.         if (!get_parm (3, background))
  268.             background = _background;
  269.  
  270.         version (old_path);
  271.  
  272.         if (old_path != "OS/2")
  273.             background = 0;
  274.  
  275.         if (!continuation && inq_modified ())
  276.         {
  277.             int    old_msg_level;
  278.  
  279.             old_msg_level = inq_msg_level ();
  280.             set_msg_level (0);
  281.             ret_code = write_buffer ();
  282.             set_msg_level (old_msg_level);
  283.         }
  284.  
  285.         if (ret_code > 0)
  286.         {
  287.             /*
  288.              *        Now we parse the filename off the path string,
  289.              *    making sure to handle the possible presence of forward
  290.              *    and backward slash characters.  We then replace the
  291.              *    file_name's ".c" with ".err" for redirection purposes.
  292.               */
  293.  
  294.             path = substr (path, 1, rindex (path, substr (path, 3, 1)));
  295.  
  296.             if (strlen (path) > 3)
  297.                 path = substr (path, 1, strlen (path) - 1);
  298.  
  299.             file_name = substr (file_name, 1, index (file_name, ".") - 1);
  300.             error_file = file_name + ".err";
  301.             ret_code = 0;
  302.  
  303.             /*
  304.              *        We want the .obj file to end up in the file's
  305.              *    directory, so we change to the directory where the
  306.              *    file is, saving the current directory.  We also make
  307.              *    the file's drive the default drive.
  308.               */
  309.  
  310.             getwd ("", command_line);
  311.             getwd (path, old_path);
  312.             old_path = substr (command_line, 1, 1) + substr (old_path, 2);
  313.             cd (path);
  314.             cd (substr (path, 1, 2));
  315.  
  316.             /*
  317.              *        This loop goes through each pass of the compiler,
  318.              *    checks to see if the return code was OK, and, if so,
  319.              *    continues along.    If an error occurs, the loop exits
  320.              *    immediately.
  321.               */
  322.  
  323.             while (!ret_code && strlen (pass_string))
  324.             {
  325.                 if (loc = search_string ("[~\\\\];", pass_string))
  326.                 {
  327.                     command_line = substr (pass_string, 1, loc);
  328.                     pass_string = substr (pass_string, loc + 2);
  329.                 }
  330.                 else
  331.                 {
  332.                     command_line = pass_string;
  333.                     pass_string = "";
  334.                 }
  335.                 command_line += " >&" + error_file;
  336.  
  337.                 while (loc = index (command_line, "\\;"))
  338.                     command_line = substr (command_line, 1, loc - 1) + substr (command_line, loc + 1);
  339.  
  340.                 /*
  341.                  *        Since environment variables don't allow "=" characters to
  342.                  *    appear in them, we use "--" as an alias.
  343.                   */
  344.  
  345.                 while (loc = index (command_line, "--"))
  346.                     command_line = substr (command_line, 1, loc - 1) + "=" + substr (command_line, loc + 2);
  347.  
  348.                 sprintf (command_line, command_line, file_name, file_name, file_name, file_name, file_name, file_name, file_name);
  349.                 message ("%s", background ? command_line + " &" : command_line);
  350.  
  351.                 if (background)
  352.                 {
  353.                     string completion_rtn;
  354.  
  355.                     inq_names (file_name);
  356.                     sprintf (completion_rtn, "_cc_complete \"%s\" %d \"%s\"", file_name, check_warnings, escape_re (pass_string, "\""));
  357.                     dos (command_line, 0, completion_rtn);
  358.                     break;
  359.                 }
  360.                 else if ((ret_code = dos (command_line, 0)) > 0)
  361.                     next_error ();
  362.                 else if (ret_code == 0 && check_warnings)
  363.                     ret_code = next_error (2);
  364.             }
  365.             /*
  366.              *         Finally, we restore the old directory.  If the
  367.              *     compilation did not succeed, the next_error macro
  368.              *     was called to place the cursor under the error.
  369.              *
  370.              *         Otherwise, the temporary file is deleted and a
  371.              *     message is printed.
  372.               */
  373.  
  374.             if (!background && ret_code <= 0)
  375.             {
  376.                 del (error_file);
  377.  
  378.                 if (ret_code == 0)
  379.                     message ("Compilation successful.");
  380.             }
  381.             cd (substr (old_path, 3));
  382.             cd (substr (old_path, 1, 2));
  383.         }
  384.     }
  385.     returns (ret_code);
  386. }
  387.  
  388. /*
  389.  *        _cc_complete:
  390.  *
  391.  *        This is the completion routine for background compilation.
  392.  */
  393.  
  394. void _cc_complete (int ret_code, string full_name, int check_warnings, string pass_string)
  395. {
  396.     int    in_memory,
  397.             old_buf_id = inq_buffer (),
  398.             buf_id,
  399.             loc;
  400.  
  401.     string    file_name,
  402.                 extension;
  403.  
  404.     /*
  405.      *        First, we check to see if the buffer being compiled
  406.      *    is in the buffer list.
  407.       */
  408.  
  409.     in_memory = (inq_buffer (full_name) != 0);
  410.  
  411.     /*
  412.      *        After the above loop has completed, if in_memory is
  413.      *    true then the current buffer is set to the buffer
  414.      *    we're compiling.    Otherwise, we have to read it into
  415.      *    memory to prepare for a possible next_error.
  416.       */
  417.  
  418.     loc = strlen (full_name);
  419.  
  420.     while (!index ("/\\", substr (full_name, loc, 1)))
  421.         --loc;
  422.  
  423.     file_name = substr (full_name, loc + 1);
  424.  
  425.     if (!in_memory)
  426.         if (buf_id = create_buffer (file_name, full_name))
  427.             set_buffer (buf_id);
  428.         else
  429.             error ("_cc_complete:  fatal error, can't edit %s.", file_name);
  430.  
  431.     inq_names (NULL, NULL, extension);
  432.  
  433.     if (!ret_code)
  434.     {
  435.         if (check_warnings)
  436.             ret_code = next_error (3);
  437.  
  438.         if (!ret_code)
  439.             if (pass_string == "")
  440.             {
  441.                 message ("Compilation of %s successful.", file_name);
  442.                 del (substr (full_name, 1, rindex (full_name, ".")) + "err");
  443.             }
  444.             else
  445.                 cc (extension, pass_string, check_warnings, 1, 1);
  446.     }
  447.     /*
  448.      *     At this point, we know we can clean up.  First, we set
  449.      *    the current buffer to the one we started with.    Then, if we
  450.      *    temporarily edited the buffer we were compiline, we delete
  451.      *    it.
  452.       */
  453.  
  454.     set_buffer (old_buf_id);
  455.  
  456.     if (!in_memory)
  457.         delete_buffer (buf_id);
  458.  
  459.     if (ret_code)
  460.     {
  461.         beep ();
  462.  
  463.         if (buf_id == 0 || old_buf_id == buf_id)
  464.             error ("Error compiling %s; press %s.", file_name, inq_assignment ("next_error", 1));
  465.         else
  466.             error ("Error compiling %s.", file_name, inq_assignment ("next_error", 1));
  467.     }
  468. }
  469.  
  470. /*
  471.  *     cm:
  472.  *
  473.  *     This macro compiles the macro in the current buffer (if there is one).
  474.  *    If the compilation was successful, the macro is re-loaded (or loaded, as
  475.  *    the case may be).  If the compilation failed, the errorfix macro
  476.  *    is used to locate the problem in the .m file.
  477.  *
  478.  *     Note that the "cc" macro is used to do most of the work.  This macro
  479.  *    merely calls "cc" with the parameters required for compilation of a
  480.  *    macro file, then loads the file if the compilation was successful.
  481.  */
  482.  
  483. int cm (...)
  484. {
  485.     int    check_warnings,
  486.             curr_parm,
  487.             ret_code;
  488.  
  489.     string    command_line = "cm";
  490.  
  491.     for (curr_parm = 1 ; curr_parm >= 0 && !get_parm (curr_parm, check_warnings) ; curr_parm--);
  492.  
  493.     if (curr_parm > 0)
  494.         get_parm (0, command_line);
  495.  
  496.     command_line += " %s";
  497.  
  498.     if (!(ret_code = cc (command_line, "m", check_warnings, 0)))
  499.     {
  500.         int    old_msg_level;
  501.  
  502.         string    name;
  503.  
  504.         inq_names (name);
  505.         name = substr (name, 1, rindex (name, ".") - 1);
  506.         old_msg_level = inq_msg_level ();
  507.         set_msg_level (3);
  508.         delete_macro (name);
  509.         set_msg_level (old_msg_level);
  510.         load_macro (name);
  511.         message ("Macro compiled and loaded.");
  512.     }
  513.     returns (ret_code);
  514. }
  515.  
  516. /*
  517.  *     cb:
  518.  *
  519.  *     This macro compiles the CBRIEF macro in the current buffer (if
  520.  *    there is one).  If the compilation was successful, the macro is
  521.  *    re-loaded (or loaded, as the case may be).  If the compilation failed,
  522.  *    the errorfix macro is used to locate the problem in the .m file.
  523.  *
  524.  *     Note that the "cc" macro is used to do most of the work.  This macro
  525.  *    merely calls "cc" with the parameters required for compilation of a
  526.  *    CBRIEF file, then loads the file if the compilation was successful.
  527.  */
  528.  
  529. int cb (...)
  530. {
  531.     int    check_warnings,
  532.             curr_parm,
  533.             ret_code;
  534.  
  535.     string    command_line = "cb";
  536.  
  537.     for (curr_parm = 1 ; curr_parm >= 0 && !get_parm (curr_parm, check_warnings) ; curr_parm--);
  538.  
  539.     if (curr_parm > 0)
  540.         get_parm (0, command_line);
  541.  
  542.     command_line += " %s";
  543.  
  544.     if (!(ret_code = cc (command_line, "cb", check_warnings, 0)))
  545.     {
  546.         int    old_msg_level;
  547.  
  548.         string    name;
  549.  
  550.         inq_names (name);
  551.         name = substr (name, 1, rindex (name, ".") - 1);
  552.         old_msg_level = inq_msg_level ();
  553.         set_msg_level (3);
  554.         delete_macro (name);
  555.         set_msg_level (old_msg_level);
  556.         load_macro (name);
  557.         message ("CBRIEF macro compiled and loaded.");
  558.     }
  559.     returns (ret_code);
  560. }
  561.